package com.example.demo;

import XPStruct.C2SStruct;
import XPStruct.S2CExecV;
import XPStruct.SeatI;
import XPStruct.XProcessI;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.geometry.Bounds;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.input.Dragboard;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.Node;
import javafx.stage.FileChooser;
import javafx.stage.Popup;
import javafx.util.StringConverter;
import javafx.stage.Stage;

import org.json.JSONObject;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import org.controlsfx.control.CheckComboBox;

public class HelloController {
    // @FXML
    // private ComboBox<SeatI> listCtrl_SeatS;
    @FXML
    private Button buttonCtrl_SeatBind;
    @FXML
    private ComboBox<XProcessI> listCtrl_XProcessS;
    @FXML
    private TextArea textCtrl_LogCtrl;
    @FXML
    private TextField textCtrl_user;
    @FXML
    private Button dropButton_user;

    @FXML
    private TextField editCtrl_suid;
    @FXML
    private TextField editCtrl_cid; // 会话ID
    @FXML
    private TextField editCtrl_mid; // 消息ID
    @FXML
    private TextField editCtrl_msgText; // 消息内容, 即将发出的
    @FXML
    private TextField editCtrl_aweme_id; // 作品ID
    @FXML
    private TextField editCtrl_author_id; // 作者ID
    @FXML
    private TextField editCtrl_comment; // 评论内容

    @FXML
    private TextField filePathField;

    private final ListView<String> listView_user = new ListView<>();
    private final Popup popup = new Popup();
    private final ObservableList<String> userList = FXCollections.observableArrayList();

    /// 多机位绑定
    @FXML
    private CheckComboBox<SeatI> comboBox_seatS;
    // @FXML
    // private CheckComboBox<XProcessI> comboBox_xProcessS;

    // 初始化方法
    @FXML
    public void initialize() {
        // 初始化机位选择器
        for (int i = 0; i < 100; i++) { // 插件中, 最高100
            comboBox_seatS.getItems().add(new SeatI(i));
        }
        // 设置默认选中
        comboBox_seatS.getCheckModel().check(1);

        // 下拉框, 字体设置
        // comboBox_seatS.setStyle("-fx-font-family: 'Courier New'; -fx-font-size:
        // 12px;");
        listCtrl_XProcessS.setStyle("-fx-font-family: 'Courier New'; -fx-font-size: 12px;");

        // 日志窗口设置
        textCtrl_LogCtrl.setEditable(true);
        textCtrl_LogCtrl.setFont(new Font("Courier New", 13));

        // 用户ID编辑框
        // editCtrl_uid.setFont(new Font("Courier New", 13));
        editCtrl_suid.setFont(new Font("Courier New", 13));

        // 会话相关
        editCtrl_cid.setFont(new Font("Courier New", 13));
        editCtrl_mid.setFont(new Font("Courier New", 13));

        // 作品相关
        editCtrl_aweme_id.setFont(new Font("Courier New", 13));
        editCtrl_author_id.setFont(new Font("Courier New", 13));
        editCtrl_comment.setFont(new Font("Courier New", 13));

        // 启用文件拖放功能
        setupDragAndDrop();
        setupUserSelected();

    }

    // 下拉时, 主动刷新已连接的进程
    @FXML
    private void onRefreshXProcessS2Ctrl() {
        // 获取当前选中的项以便恢复选择
        XProcessI selectedItem = listCtrl_XProcessS.getSelectionModel().getSelectedItem();

        // 1. 清除现有项
        ObservableList<XProcessI> items = listCtrl_XProcessS.getItems();
        items.clear();

        // 2. 获取所有绑定的WebSocket实例
        List<XPWebSocket> wsList = getBindSeatList();
        System.out.println("获取到 " + wsList.size() + " 个WebSocket实例");

        // 3. 确保UI组件已初始化
        if (listCtrl_XProcessS == null) {
            System.err.println("错误：listCtrl_XProcessS 为null");
            return;
        }

        // 4. 添加新数据
        for (XPWebSocket ws : wsList) {
            List<XProcessI> processes = ws.GetXprocessS();
            System.out.println("从实例获取 " + processes.size() + " 个进程");

            // 确保在JavaFX应用线程上更新UI
            Platform.runLater(() -> {
                items.addAll(processes);
                System.out.println("已添加 " + processes.size() + " 个进程到列表");
            });
        }

        // 5. 调试输出最终结果
        Platform.runLater(() -> {
            System.out.println("列表当前项数: " + items.size());
            if (items.isEmpty()) {
                System.out.println("警告：列表仍然为空");
            }
        });
        _logString("--------------");
        _logString(null == items ? "无傀儡进程" : "当前机位绑定的傀儡进程数量 = " + items.size());
        _logString("--------------");
        // 尝试恢复之前的选择
        if (selectedItem != null && items.contains(selectedItem)) {
            listCtrl_XProcessS.getSelectionModel().select(selectedItem);
        } else {
            // 如果之前的选择不存在，则选择第一个项
            if (!items.isEmpty()) {
                listCtrl_XProcessS.getSelectionModel().selectFirst();
            }
        }
    }

    // 绑定机位, 创建WebSocket服务端

    private List<SeatI> getSeatList() {
        return comboBox_seatS.getCheckModel().getCheckedItems();
    }

    @FXML
    protected void onButtonClickByBindWSP() {
        // 获取选中的机位
        List<SeatI> seatList = getSeatList();

        if (null == seatList)
            return;
        for (SeatI seatI : seatList) {
            onBindWSP(seatI);
        }
        // 禁用机位选择器
        comboBox_seatS.setDisable(true);
        buttonCtrl_SeatBind.setDisable(true);
    }

    private void onBindWSP(SeatI seatInfo) {

        if (null == seatInfo || 0 == seatInfo.getPort())
            return;
        // 安装唯一的对象
        XPWebSocket ws = XPWebSocket.installInstance(seatInfo.getPort());
        //
        ws.start();
    }

    private List<XPWebSocket> getBindSeatList() {
        return getSeatList().stream()
                .map(seatI -> XPWebSocket.getInstance(seatI.getPort()))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

    }

    // 选择当前选中的傀儡进程
    private String SeleteXProcessUID() {
        // 获取选中的机位
        XProcessI xProcessI = listCtrl_XProcessS.getSelectionModel().getSelectedItem();
        if (null == xProcessI)
            return "";
        //
        return xProcessI.uniqueID;
    }

    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################

    // 弹窗, 异常弹窗
    private void AlertByE(String eString) {
        Alert alert = new Alert(AlertType.INFORMATION);
        alert.setTitle("功能执行异常");
        alert.setHeaderText(eString);
        List<XPWebSocket> wsList = getBindSeatList();
        for (XPWebSocket xpWebSocket : wsList) {
            if (null != xpWebSocket) // 展示异常堆栈内容
                alert.setContentText(xpWebSocket.lastEStackTrace);

            alert.showAndWait(); // 阻塞，直到用户关闭对话框
            return;
        }

    }

    private String _2FormatS(String format, Object... args) {
        if (format == null) {
            return "";
        }
        return String.format(format, args);
    }

    private void _logClear() {
        // 在GUI线程中更新TextArea
        Platform.runLater(() -> {
            textCtrl_LogCtrl.setText("");
        });
    }

    private void _logString(String... args) {
        String lString = "";

        for (int i = 0; i < args.length; i++) {
            String log = args[i];
            //
            lString += log;

            if (1 != args.length && i != args.length - 1)
                lString += (", ");
        }
        // 追加一个换行符,方便查看
        lString += ("\r\n");
        // 在GUI线程中更新TextArea
        String finalLString = lString;
        Platform.runLater(() -> {
            textCtrl_LogCtrl.appendText(finalLString);
        });
    }

    private void _logStringLINE(int count) {
        for (int i = 0; i < count; i++) {
            _logString("---------------------------------------------------------------------------------------------");
        }
    }

    private void _logFormat(String format, Object... args) {
        if (format == null) {
            return;
        }
        _logString(String.format(format, args));
    }

    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################

    // 按钮事件 : 通讯测试
    @FXML
    protected void onButtonClickByHello() throws InterruptedException {
        List<XPWebSocket> wsList = getBindSeatList();
        for (XPWebSocket xpWebSocket : wsList) {
            onHello(xpWebSocket);
        }

    }

    private void onHello(XPWebSocket ws) {
        //
        XPWebSocket XSocket = null;
        if (null == (XSocket = ws)) {
            AlertByE("机位未绑定");
            return;
        }
        //
        String uniqueID = "";
        if ((uniqueID = SeleteXProcessUID()).isEmpty()) {
            AlertByE("选择傀儡进程异常");
            return;
        }
        // 执行测试代码, 并且处理返回值
        XSocket.NETSendAction2XP(uniqueID, "ts.debug.string", result -> {
            _logClear();
            _logString("错误代码", result.error);
            _logStringLINE(1);
            _logString("返回数据", XPWebSocket.bytesToHex(result.retv));
            _logStringLINE(1);
            _logString("测试结果", result.ConvertByString());
        }, "1测2试3通4讯5");
    }

    // 按钮事件 : 默认参数
    @FXML
    protected void onButtonClickByTTParamS() {
        List<XPWebSocket> wsList = getBindSeatList();
        for (XPWebSocket xpWebSocket : wsList) {
            onTTParamS(xpWebSocket);
        }
    }

    private void onTTParamS(XPWebSocket ws) {
        //
        XPWebSocket XSocket = null;
        if (null == (XSocket = ws)) {
            AlertByE("机位未绑定");
            return;
        }
        //
        String uniqueID = "";
        if ((uniqueID = SeleteXProcessUID()).isEmpty()) {
            AlertByE("选择傀儡进程异常");
            return;
        }
        // 执行测试代码, 并且处理返回值
        XSocket.NETSendAction2XP(uniqueID, "aweme.tt.common.param", result -> {
            _logClear();
            _logString("错误代码", result.error);
            _logStringLINE(1);
            _logString("返回数据", XPWebSocket.bytesToHex(result.retv));
            _logStringLINE(1);

            // 解析返回数据
            JSONObject JSONValueS = new JSONObject(result.ConvertByString());
            //
            Iterator<String> keys = JSONValueS.keys();

            while (keys.hasNext()) {
                String key = keys.next();
                Object value = JSONValueS.get(key);

                _logFormat("%-20s : %s", key,
                        value.toString());
            }
        });
    }

    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################

    // 打印用户信息
    protected String onButtonByUserOUT(String userString) {
        try {
            // 解析用户数据
            JSONObject JSONUser = new JSONObject(userString);

            // 分割
            _logStringLINE(1);

            // 当前数据库内容
            String blockString = "";

            String secUserID = JSONUser.get("secUserID").toString();

            blockString += _2FormatS("customID          = %s \r\n", JSONUser.get("customID").toString());
            blockString += _2FormatS("nickname          = %s \r\n", JSONUser.get("nickname").toString());
            blockString += "\r\n";
            blockString += _2FormatS("userID            = %s \r\n", JSONUser.get("userID").toString());
            blockString += _2FormatS("secUserID         = %s \r\n", secUserID);

            blockString += "\r\n";
            blockString += _2FormatS("json              = %s \r\n", userString);

            _logString(blockString);
            return secUserID;
        } catch (Exception e) {
            return "";
        }
    }

    // 按钮事件 : 用户列表, 全部用户
    @FXML
    protected void onButtonClickByUserList() {
        List<XPWebSocket> wsList = getBindSeatList();
        for (XPWebSocket xpWebSocket : wsList) {
            onUserList(xpWebSocket);
        }
    }

    private void onUserList(XPWebSocket ws) {
        //
        XPWebSocket XSocket = null;
        if (null == (XSocket = ws)) {
            AlertByE("机位未绑定");
            return;
        }
        //
        String uniqueID = "";
        if ((uniqueID = SeleteXProcessUID()).isEmpty()) {
            AlertByE("选择傀儡进程异常");
            return;
        }
        // 执行测试代码, 并且处理返回值
        XSocket.NETSendAction2XP(uniqueID, "tiktok.user.list", result -> {
            _logClear();
            _logString("错误代码", result.error);
            _logStringLINE(1);
            _logString("返回数据", XPWebSocket.bytesToHex(result.retv));

            ArrayList<String> userS = result.ConvertByStringArray();
            if (null == userS || userS.isEmpty()) {
                AlertByE("用户为空, 或, 解析失败");
                return;
            }
            // 解析每一个用户
            userS.forEach(this::onButtonByUserOUT);
        });
    }

    // 按钮事件 : 用户列表, 当前用户
    @FXML
    protected void onButtonClickByUserCurrent() {
        List<XPWebSocket> wsList = getBindSeatList();
        for (XPWebSocket xpWebSocket : wsList) {
            onUserCurrent(xpWebSocket);
        }
    }

    private void onUserCurrent(XPWebSocket ws) {
        //
        XPWebSocket XSocket = null;
        if (null == (XSocket = ws)) {
            AlertByE("机位未绑定");
            return;
        }
        //
        String uniqueID = "";
        if ((uniqueID = SeleteXProcessUID()).isEmpty()) {
            AlertByE("选择傀儡进程异常");
            return;
        }
        // 执行测试代码, 并且处理返回值
        XSocket.NETSendAction2XP(uniqueID, "tiktok.user.current", result -> {
            _logClear();
            _logString("错误代码", result.error);
            _logStringLINE(1);
            _logString("返回数据", XPWebSocket.bytesToHex(result.retv));

            String userI = result.ConvertByString();
            if (null == userI || userI.isEmpty()) {
                AlertByE("用户为空, 或, 解析失败");
                return;
            }
            // 解析每一个用户
            String secUserId = onButtonByUserOUT(userI);
            editCtrl_suid.setText(secUserId);
        });
    }

    // 按钮事件 : 用户列表, 当前用户
    @FXML
    protected void onButtonClickByUserSwitch() {
        List<XPWebSocket> wsList = getBindSeatList();
        for (XPWebSocket xpWebSocket : wsList) {
            onUserSwitch(xpWebSocket);
        }
    }

    private void onUserSwitch(XPWebSocket ws) {
        //
        XPWebSocket XSocket = null;
        if (null == (XSocket = ws)) {
            AlertByE("机位未绑定");
            return;
        }
        //
        String uniqueID = "";
        if ((uniqueID = SeleteXProcessUID()).isEmpty()) {
            AlertByE("选择傀儡进程异常");
            return;
        }
        //
        String uid, suid = "";
        String getUid = getUserId();
        if ((uid = getUid.trim()).isEmpty()) {
            AlertByE("输入异常, uid");
            return;
        }

        // 执行测试代码, 并且处理返回值
        XSocket.NETSendAction2XP(uniqueID, "tiktok.user.switch", result -> {
            _logClear();
            _logString("错误代码", result.error);
            _logStringLINE(1);
            _logString("返回数据", XPWebSocket.bytesToHex(result.retv));
            _logStringLINE(1);
            _logString("是否成功", result.ConvertByBoolen() ? "true" : "false");
            _logStringLINE(1);
            _logString("特别注意", "这里只代表, 执行成功, 切换是否成功需要使用 当前用户 进行检测");
        }, uid);
    }

    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################

    // 按钮事件 : 会话操作, 全部会话
    @FXML
    protected void onButtonClickByConversationList() {
        List<XPWebSocket> wsList = getBindSeatList();
        for (XPWebSocket xpWebSocket : wsList) {
            onByConversationList(xpWebSocket);
        }
    }

    private void onByConversationList(XPWebSocket ws) {
        //
        XPWebSocket XSocket = null;
        if (null == (XSocket = ws)) {
            AlertByE("机位未绑定");
            return;
        }
        //
        String uniqueID = "";
        if ((uniqueID = SeleteXProcessUID()).isEmpty()) {
            AlertByE("选择傀儡进程异常");
            return;
        }
        // 执行测试代码, 并且处理返回值
        XSocket.NETSendAction2XP(uniqueID, "tiktok.tim.conversation.list", result -> {
            _logClear();
            _logString("错误代码", result.error);
            _logStringLINE(1);
            _logString("返回数据", XPWebSocket.bytesToHex(result.retv));

            ArrayList<String> conversationS = result.ConvertByStringArray();
            if (null == conversationS || conversationS.isEmpty()) {
                AlertByE("会话为空, 或, 解析失败");
                return;
            }
            _logStringLINE(1);
            // 解析每一个会话
            conversationS.forEach(conversationID -> {
                _logString("会话编号", conversationID);
            });
        });
    }

    // 按钮事件 : 会话操作, 关闭会话
    @FXML
    protected void onButtonClickByConversationClose() {
        List<XPWebSocket> wsList = getBindSeatList();
        for (XPWebSocket xpWebSocket : wsList) {
            onByConversationClose(xpWebSocket);
        }
    }

    // 按钮事件 : 会话操作, 关闭会话
    private void onByConversationClose(XPWebSocket ws) {
        //
        XPWebSocket XSocket = null;
        if (null == (XSocket = ws)) {
            AlertByE("机位未绑定");
            return;
        }
        //
        String uniqueID = "";
        if ((uniqueID = SeleteXProcessUID()).isEmpty()) {
            AlertByE("选择傀儡进程异常");
            return;
        }
        //
        String conversationID = "";
        if ((conversationID = editCtrl_cid.getText().trim()).isEmpty()) {
            AlertByE("输入异常, 会话, 会话ID, 会话编号");
            return;
        }
        // 执行测试代码, 并且处理返回值
        XSocket.NETSendAction2XP(uniqueID, "tiktok.tim.conversation.close", result -> {
            _logClear();
            _logString("错误代码", result.error);
            _logStringLINE(1);
            _logString("返回数据", XPWebSocket.bytesToHex(result.retv));
            _logStringLINE(1);
            _logString("是否成功", result.ConvertByBoolen() ? "true" : "false");
            _logStringLINE(1);
            _logString("特别注意", "这里只代表, 执行成功, 关闭是否成功需要使用 会话列表 进行检测");
        }, conversationID);
    }

    // 按钮事件 : 会话操作, 会话详情
    @FXML
    protected void onButtonClickByConversationDetail() {
        for (XPWebSocket ws : getBindSeatList()) {
            onDetail(ws);
        }
    }

    private void onDetail(XPWebSocket ws) {
        //
        XPWebSocket XSocket = null;
        if (null == (XSocket = ws)) {
            AlertByE("机位未绑定");
            return;
        }
        //
        String uniqueID = "";
        if ((uniqueID = SeleteXProcessUID()).isEmpty()) {
            AlertByE("选择傀儡进程异常");
            return;
        }
        //
        String conversationID = "";
        if ((conversationID = editCtrl_cid.getText().trim()).isEmpty()) {
            AlertByE("输入异常, 会话, 会话ID, 会话编号");
            return;
        }
        // 执行测试代码, 并且处理返回值
        XSocket.NETSendAction2XP(uniqueID, "tiktok.tim.conversation.detail", result -> {
            _logClear();
            _logString("错误代码", result.error);
            _logStringLINE(1);
            _logString("返回数据", XPWebSocket.bytesToHex(result.retv));
            _logStringLINE(1);
            // 解析返回数据
            JSONObject JSONValueS = new JSONObject(result.ConvertByString());
            //
            Iterator<String> keys = JSONValueS.keys();
            //
            while (keys.hasNext()) {
                String key = keys.next();
                Object value = JSONValueS.get(key);

                _logFormat("%-30s : %s", key,
                        value.toString());
            }
        }, conversationID);
    }

    // 按钮事件 : 会话操作, 最后消息
    @FXML
    protected void onButtonClickByConversationLaseMessage() {
        for (XPWebSocket ws : getBindSeatList()) {
            onLaseMessage(ws);
        }
    }

    private void onLaseMessage(XPWebSocket ws) {
        //
        XPWebSocket XSocket = null;
        if (null == (XSocket = ws)) {
            AlertByE("机位未绑定");
            return;
        }
        //
        String uniqueID = "";
        if ((uniqueID = SeleteXProcessUID()).isEmpty()) {
            AlertByE("选择傀儡进程异常");
            return;
        }
        //
        String conversationID = "";
        if ((conversationID = editCtrl_cid.getText().trim()).isEmpty()) {
            AlertByE("输入异常, 会话, 会话ID, 会话编号");
            return;
        }

        // 执行测试代码, 并且处理返回值
        XSocket.NETSendAction2XP(uniqueID, "tiktok.tim.conversation.message.last", result -> {
            _logClear();
            _logString("错误代码", result.error);
            _logStringLINE(1);
            _logString("返回数据", XPWebSocket.bytesToHex(result.retv));
            _logStringLINE(1);
            // 解析返回数据
            JSONObject JSONValueS = new JSONObject(result.ConvertByString());
            //
            Iterator<String> keys = JSONValueS.keys();
            //
            while (keys.hasNext()) {
                String key = keys.next();
                Object value = JSONValueS.get(key);

                _logFormat("%-30s : %s", key,
                        value.toString());
            }
        }, conversationID);
    }

    // 按钮事件 : 会话操作, 未读消息
    @FXML
    protected void onButtonClickByConversationUnReadCount() {
        for (XPWebSocket ws : getBindSeatList()) {
            onUnReadCount(ws);
        }
    }

    private void onUnReadCount(XPWebSocket ws) {
        //
        XPWebSocket XSocket = null;
        if (null == (XSocket = ws)) {
            AlertByE("机位未绑定");
            return;
        }
        //
        String uniqueID = "";
        if ((uniqueID = SeleteXProcessUID()).isEmpty()) {
            AlertByE("选择傀儡进程异常");
            return;
        }
        //
        String conversationID = "";
        if ((conversationID = editCtrl_cid.getText().trim()).isEmpty()) {
            AlertByE("输入异常, 会话, 会话ID, 会话编号");
            return;
        }
        // 执行测试代码, 并且处理返回值
        XSocket.NETSendAction2XP(uniqueID, "tiktok.tim.conversation.message.unread", result -> {
            _logClear();
            _logString("错误代码", result.error);
            _logStringLINE(1);
            _logString("返回数据", XPWebSocket.bytesToHex(result.retv));
            _logStringLINE(1);
            _logFormat("未读消息 = %d", result.ConvertByLong());
        }, conversationID);
    }

    // 按钮事件 : 会话操作, 标记已读
    @FXML
    protected void onButtonClickByConversationMaskRead() {
        for (XPWebSocket ws : getBindSeatList()) {
            onMaskRead(ws);
        }
    }

    private void onMaskRead(XPWebSocket ws) {
        //
        XPWebSocket XSocket = null;
        if (null == (XSocket = ws)) {
            AlertByE("机位未绑定");
            return;
        }
        //
        String uniqueID = "";
        if ((uniqueID = SeleteXProcessUID()).isEmpty()) {
            AlertByE("选择傀儡进程异常");
            return;
        }
        //
        String conversationID = "";
        if ((conversationID = editCtrl_cid.getText().trim()).isEmpty()) {
            AlertByE("输入异常, 会话, 会话ID, 会话编号");
            return;
        }
        // 执行测试代码, 并且处理返回值
        XSocket.NETSendAction2XP(uniqueID, "tiktok.tim.conversation.message.mark.is.read", result -> {
            _logClear();
            _logString("错误代码", result.error);
            _logStringLINE(1);
            _logString("返回数据", XPWebSocket.bytesToHex(result.retv));
            _logStringLINE(1);
            _logString("是否成功", result.ConvertByBoolen() ? "true" : "false");
            _logStringLINE(1);
            _logString("特别注意", "这里只代表, 执行成功, 标记是否成功需要使用 未读数目 进行检测");
        }, conversationID);
    }

    // 按钮事件 : 会话操作, 消息列表
    @FXML
    protected void onButtonClickByConversationMessageList() {
        for (XPWebSocket ws : getBindSeatList()) {
            onMessageList(ws);
        }
    }

    private void onMessageList(XPWebSocket ws) {
        //
        XPWebSocket XSocket = null;
        if (null == (XSocket = ws)) {
            AlertByE("机位未绑定");
            return;
        }
        //
        String uniqueID = "";
        if ((uniqueID = SeleteXProcessUID()).isEmpty()) {
            AlertByE("选择傀儡进程异常");
            return;
        }
        //
        String conversationID = "";
        if ((conversationID = editCtrl_cid.getText().trim()).isEmpty()) {
            AlertByE("输入异常, 会话, 会话ID, 会话编号");
            return;
        }
        // 执行测试代码, 并且处理返回值
        XSocket.NETSendAction2XP(uniqueID, "tiktok.tim.conversation.message.list", result -> {
            _logClear();
            _logString("错误代码", result.error);
            _logStringLINE(1);
            _logString("返回数据", XPWebSocket.bytesToHex(result.retv));
            _logStringLINE(1);
            // 解析返回数据
            ArrayList<String> messageIDS = result.ConvertByStringArray();
            //
            messageIDS.forEach(messageID -> {
                _logFormat("%s", messageID);
            });
        }, conversationID);
    }

    // 按钮事件 : 会话操作, 消息内容
    @FXML
    protected void onButtonClickByConversationGetMessage() {
        for (XPWebSocket ws : getBindSeatList()) {
            onGetMessage(ws);
        }
    }

    private void onGetMessage(XPWebSocket ws) {
        //
        XPWebSocket XSocket = null;
        if (null == (XSocket = ws)) {
            AlertByE("机位未绑定");
            return;
        }
        //
        String uniqueID = "";
        if ((uniqueID = SeleteXProcessUID()).isEmpty()) {
            AlertByE("选择傀儡进程异常");
            return;
        }
        //
        String conversationID = "";
        if ((conversationID = editCtrl_cid.getText().trim()).isEmpty()) {
            AlertByE("输入异常, 会话, 会话ID, 会话编号");
            return;
        }
        String messageID = "";
        if ((messageID = editCtrl_mid.getText().trim()).isEmpty()) {
            AlertByE("输入异常, 消息, 消息ID, 消息编号");
            return;
        }
        // 执行测试代码, 并且处理返回值 , 注意: 此地还支持 tiktok.tim.conversation.message.local.get ,
        // 本地消息ID获取
        XSocket.NETSendAction2XP(uniqueID, "tiktok.tim.conversation.message.server.get", result -> {
            _logClear();
            _logString("错误代码", result.error);
            _logStringLINE(1);
            _logString("返回数据", XPWebSocket.bytesToHex(result.retv));
            _logStringLINE(1);
            // 解析返回数据
            JSONObject JSONValueS = new JSONObject(result.ConvertByString());
            //
            Iterator<String> keys = JSONValueS.keys();
            //
            while (keys.hasNext()) {
                String key = keys.next();
                Object value = JSONValueS.get(key);

                _logFormat("%-30s : %s", key,
                        value.toString());
            }
        }, conversationID, messageID);
    }

    // 按钮事件 : 会话操作, 发送消息, 文本类型
    @FXML
    protected void onButtonClickByConversationSendMessageText() {
        List<XPWebSocket> wsList = getBindSeatList();
        for (XPWebSocket xpWebSocket : wsList) {
            onSendMessageText(xpWebSocket);
        }
    }

    private void onSendMessageText(XPWebSocket ws) {
        //
        XPWebSocket XSocket = null;
        if (null == (XSocket = ws)) {
            AlertByE("机位未绑定");
            return;
        }
        //
        String uniqueID = "";
        if ((uniqueID = SeleteXProcessUID()).isEmpty()) {
            AlertByE("选择傀儡进程异常");
            return;
        }
        //
        String user_ID = "";
        if ((user_ID = getUserId().trim()).isEmpty()) {
            AlertByE("输入异常, 用户, 用户ID, 用户编号, uid");
            return;
        }
        String msgText = "";
        if ((msgText = editCtrl_msgText.getText().trim()).isEmpty()) {
            AlertByE("输入异常, 私聊的文本内容信息, 禁止空内容");
            return;
        }
        // 执行测试代码, 并且处理返回值
        XSocket.NETSendAction2XP(uniqueID, "tiktok.tim.message.send.text", result -> {
            _logClear();
            _logString("错误代码", result.error);
            _logStringLINE(1);
            _logString("返回数据", XPWebSocket.bytesToHex(result.retv));
            _logStringLINE(1);
            // 解析返回数据
            ArrayList<String> idS = result.ConvertByStringArray();
            if (null == idS || 2 != idS.size()) {
                _logString("解析异常");
                return;
            }
            //
            _logString("消息编号", idS.removeFirst());
            _logString("会话编号", idS.removeFirst());
            _logStringLINE(1);
            _logString("本地编号", "可使用 tiktok.tim.message.get                      查询详情");
            _logString("本地编号", "可使用 tiktok.tim.conversation.message.local.get   查询详情");
            //
        }, user_ID, msgText, 703/* aweType = 703为默认 */);
    }

    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################

    // 默认
    private static String HOST_API_VA = "https://api-va.tiktokv.com";

    class HTTPResponseI {
        long code;

        String url;

        String data;

        JSONObject log;
        JSONObject headS;

        String error;

        JSONObject json;

        HTTPResponseI(JSONObject j) {
            json = j;

            code = j.getLong("code");
            url = j.getString("url");
            data = j.getString("data");
            error = j.getString("error");

            log = new JSONObject(j.getString("log"));
            headS = new JSONObject(j.getString("headS"));

            // data, 默认为 base64 编码, 解码重写即可
            data = new String(Base64.getDecoder().decode(data));
        }

        HTTPResponseI() {
        }

        @Override
        public String toString() {
            String lString = "";

            lString += _2FormatS("请求状态返回 = %d", code);
            lString += "\r\n";
            lString += _2FormatS("请求链接地址 = %s", url);
            lString += "\r\n";
            lString += _2FormatS("请求结果返回 = %s", data);
            lString += "\r\n";
            lString += _2FormatS("请求任务日志 = %s", log.toString());
            lString += "\r\n";

            return lString;
        }

    }

    // 定义一个回调接口
    interface HACallback {
        void onComplete(HTTPResponseI result);
    }

    private HTTPResponseI http_response(XPWebSocket XSocket, String uniqueID, long taskID, long otime)
            throws InterruptedException {
        long TStartTIME = XPWebSocket._2TIMEStamp();
        while (0 != taskID && XPWebSocket._2TIMEStamp() - TStartTIME <= otime) {
            // 主动延迟
            Thread.sleep(100);
            try {
                // 创建一个 CountDownLatch，初始值为 1
                CountDownLatch latch = new CountDownLatch(1);
                //
                AtomicReference<String> JSONString = new AtomicReference<>();
                // 获取响应结果
                S2CExecV retval = XSocket.NETSendAction2XP(uniqueID, "aweme.tt.http.response", result -> {
                    // 写入返回值
                    JSONString.set(result.ConvertByString());
                    // 任务完成后，减少计数器的值
                    latch.countDown();
                }, taskID);
                // 等待直到回调被执行
                if (retval != null && !retval.guid.isEmpty())
                    latch.await();
                // 检测是否已完成写入
                if (null == JSONString.get() || JSONString.get().isEmpty())
                    continue;
                // 转换成json格式返回即可
                return new HTTPResponseI(new JSONObject(JSONString.get()));
            } catch (InterruptedException e) {
            }
        }
        return new HTTPResponseI(); // 响应超时
    }

    // http 的事件处理流程
    private void http_action(String hostString, String uriString, String method, JSONObject bodyJSON,
            JSONObject headJSON,
            String decrypt, HACallback xCallback) {
        List<XPWebSocket> wsList = getBindSeatList();
        for (XPWebSocket xpWebSocket : wsList) {
            each_http_action(xpWebSocket, hostString, uriString, method, bodyJSON, headJSON, decrypt, xCallback);
        }
    }

    private void each_http_action(XPWebSocket ws, String hostString, String uriString, String method,
            JSONObject bodyJSON,
            JSONObject headJSON,
            String decrypt, HACallback xCallback) {
        //
        XPWebSocket XSocket = null;
        if (null == (XSocket = ws)) {
            AlertByE("机位未绑定");
            return;
        }
        //
        String uniqueID = "";
        if ((uniqueID = SeleteXProcessUID()).isEmpty()) {
            AlertByE("选择傀儡进程异常");
            return;
        }
        // 修正方法名称
        if (method.equals("get"))
            method = "aweme.tt.http.get";
        if (method.equals("post"))
            method = "aweme.tt.http.post";

        // 执行参数列表
        String args_url = hostString + uriString;
        String args_body = bodyJSON.toString();
        String args_head = headJSON.toString();
        //
        XPWebSocket finalXSocket = XSocket;
        String finalUniqueID = uniqueID;
        // 执行测试代码, 并且处理返回值
        XSocket.NETSendAction2XP(uniqueID, method, result -> {
            //
            long taskID = 0;
            //
            _logClear();
            _logString("错误代码", result.error);
            _logStringLINE(1);
            _logString("返回数据", XPWebSocket.bytesToHex(result.retv));
            _logStringLINE(1);
            _logString("任务编号", _2FormatS("%-5d, 用来获取响应结果!!",
                    taskID = result.ConvertByLong()));
            _logStringLINE(1);
            //
            long finalTaskID = taskID;
            // 异步执行, 等待任务返回, 并且将响应结果传入回调函数
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                try {
                    xCallback.onComplete(http_response(finalXSocket, finalUniqueID,
                            finalTaskID, 30 * 1000));
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
            // 任务完成后的回调
            future.thenRun(() -> {
            });
            //
        }, args_url, args_body, args_head, decrypt);
    }

    // http post 请求
    private void http_post(String hostString, String uriString, JSONObject bodyJSON, JSONObject headJSON,
            String decrypt, HACallback xCallback) {
        http_action(hostString, uriString, "post", bodyJSON, headJSON, decrypt, xCallback);
    }

    // http get 请求
    private void http_get(String hostString, String uriString, JSONObject bodyJSON, JSONObject headJSON, String decrypt,
            HACallback xCallback) {
        http_action(hostString, uriString, "get", bodyJSON, headJSON, decrypt, xCallback);
    }

    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################

    // 参数构造
    String arg_experiment_params() {
        return "[{\"key\":\"inbox_tts_entrance_v3\",\"value\":\"2\"},{\"key\":\"nickname_instead_of_username\",\"value\":\"2\"},{\"key\":\"tt_im_resend_failed_msg_permission_change\",\"value\":\"1\"},{\"key\":\"enable_inbox_activity_classification\",\"value\":\"0\"},{\"key\":\"tt_merge_follow_request_into_new_follower\",\"value\":\"1\"},{\"key\":\"use_rec_notice_follow_page\",\"value\":\"0\"},{\"key\":\"inbox_system_notification_unread_flag\",\"value\":\"3\"},{\"key\":\"inbox_redesign_v1_adjustment_type\",\"value\":\"7\"},{\"key\":\"alias_consumption_switch\",\"value\":\"1\"},{\"key\":\"inbox_activity_load_optimize_strategy\",\"value\":\"1\"},{\"key\":\"inbox_system_notification_message_box\",\"value\":\"2\"},{\"key\":\"enable_activity_priority\",\"value\":\"1\"},{\"key\":\"tiktok_ai_creator_assistant_entrance\",\"value\":\"0\"},{\"key\":\"new_followers_preload\",\"value\":\"1\"},{\"key\":\"inbox_redesign_optimize_phase_3\",\"value\":\"0\"},{\"key\":\"creator_inbox_experiment\",\"value\":\"0\"},{\"key\":\"inbox_new_follow_ordering_enable\",\"value\":\"0\"},{\"key\":\"inbox_redesign_type\",\"value\":\"1\"},{\"key\":\"remind_users_dm_permission_receiver_v4\",\"value\":\"1\"},{\"key\":\"tt_enable_im_snail_app_ug_notice\",\"value\":\"0\"},{\"key\":\"inbox_style_opt\",\"value\":\"0\"},{\"key\":\"system_notice_unsubscribe_experiment\",\"value\":\"1\"},{\"key\":\"inbox_creator_notification_entrance_style\",\"value\":\"0\"},{\"key\":\"enable_tako_entrance\",\"value\":\"0\"}]";
    }

    // 参数构造
    String arg_additional_group_list() {
        return "[{\"group\":210,\"min_time\":0,\"is_mark_read\":0,\"count\":1,\"max_time\":0}]";
    }

    // 按钮事件 : 获取 通知(系统消息)列表
    @FXML
    protected void onButtonClickBySystemNotice() {
        //
        String uri = "/tiktok/notice/entrance/list/v1/";
        String decrypt = "";
        //
        JSONObject bodyJSON = new JSONObject();
        JSONObject headJSON = new JSONObject();

        bodyJSON.put("additional_group_list", arg_additional_group_list());
        bodyJSON.put("experiment_params", arg_experiment_params());
        bodyJSON.put("need_latest_notice_time", "");

        // 开始处理请求
        http_get(HOST_API_VA, uri.trim(), bodyJSON, headJSON, decrypt.trim(), retval -> {
            _logString(retval.toString());
            _logStringLINE(1);
            // ----------------------------------------------------------------------------------------------------------
            JSONObject dataJSON = new JSONObject(retval.data);
            if (dataJSON.isNull("status_code")) {
                _logString("解析异常");
                return;
            }

            _logString(dataJSON.toString());
        });
    }

    // 按钮事件 : 获取 通知(用户消息)列表
    @FXML
    protected void onButtonClickByUserNotice() {
        _logClear();
        _logString("错误代码", "");
        _logStringLINE(1);
        _logString("返回数据", "");
        _logStringLINE(1);
        _logString("特别注意", "当前接口未封装, 已改为 用户会话, 可使用会话功能进行监控处理!!");
    }

    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################

    // 按钮事件 : 获取 作品信息, 通过探索
    @FXML
    protected void onButtonClickBySearchExplore() {
        //
        String uri = "/tiktok/feed/explore/v1";
        String decrypt = "";
        //
        JSONObject bodyJSON = new JSONObject();
        JSONObject headJSON = new JSONObject();

        bodyJSON.put("enter_from", 0);
        bodyJSON.put("non_personalized", 0);
        bodyJSON.put("pull_type", 1);
        bodyJSON.put("tab_type", 2);

        // 开始处理请求
        http_post(HOST_API_VA, uri.trim(), bodyJSON, headJSON, decrypt.trim(), retval -> {
            _logString(retval.toString());

        });
    }

    // 按钮事件 : 获取 作品信息, 通过推荐
    @FXML
    protected void onButtonClickBySearchRecommend() {
        //
        String uri = "/aweme/v2/feed/";
        String decrypt = "AWEFeedPbResponseSerializer";
        //
        JSONObject bodyJSON = new JSONObject();
        JSONObject headJSON = new JSONObject();

        // 随机
        Random random = new Random();
        // 指定范围
        long min = 1_000_000_000L; // 最小值（包含）
        long max = 9_999_999_999L; // 最大值（不包含）

        bodyJSON.put("device_memory_total", min + (long) (random.nextDouble() * (max - min)));
        bodyJSON.put("pull_type", 2);

        // 开始处理请求
        http_post(HOST_API_VA, uri.trim(), bodyJSON, headJSON, decrypt.trim(), retval -> {
            _logString(retval.toString());

        });
    }

    // 按钮事件 : 获取 作品信息, 通过STEM
    @FXML
    protected void onButtonClickBySearchSTEM() {
        //
        String uri = "/tiktok/feed/stem/v2";
        String decrypt = "TTKFeedSTEMPbResponseSerializer";
        //
        JSONObject bodyJSON = new JSONObject();
        JSONObject headJSON = new JSONObject();

        bodyJSON.put("ad_personality_mode", 1);
        bodyJSON.put("ad_user_agent", "TikTok 38.7.0 rv:387018 (iPhone; iOS 13.5.1; zh-Hans_KR) Cronet"); // 这里要求不高,
                                                                                                          // 随意即可
        bodyJSON.put("camera_status", 0);
        bodyJSON.put("cmpl_enc", UUID.randomUUID().toString());
        bodyJSON.put("count", 6);
        bodyJSON.put("feed_style", 0);
        bodyJSON.put("filter_warn", 0);
        bodyJSON.put("ima_app_status", 0);
        bodyJSON.put("os_boot_time", "1740196995971.329");
        bodyJSON.put("preferred_language", "zh-Hant-KR");
        bodyJSON.put("pull_type", 1);

        bodyJSON.put("resolution", "1125*2436");
        bodyJSON.put("sp", 1571);
        bodyJSON.put("topic_type", "stem");

        // 开始处理请求
        http_get(HOST_API_VA, uri.trim(), bodyJSON, headJSON, decrypt.trim(), retval -> {
            _logString(retval.toString());

        });
    }

    // 按钮事件 : 获取 直播信息, 通过推荐
    @FXML
    protected void onButtonClickBySearchLIVE() {
        //
        String uri = "/webcast/feed/";
        String decrypt = "";
        //
        JSONObject bodyJSON = new JSONObject();
        JSONObject headJSON = new JSONObject();

        bodyJSON.put("content_type", 0);
        bodyJSON.put("style", 2);
        bodyJSON.put("sub_channel_id", 0);
        bodyJSON.put("tab_id", 1);
        bodyJSON.put("contypetent_type", "live");
        bodyJSON.put("sub_type", "live");
        bodyJSON.put("req_type", 0);
        bodyJSON.put("cate_id", 0);
        bodyJSON.put("channel_id", 96);
        bodyJSON.put("channel_id", 96);
        bodyJSON.put("is_non_personalized", 0);
        bodyJSON.put("enter_source", "live_merge-live_cover");

        long max_time = 0; // 最高时间

        if (0 < max_time) {
            bodyJSON.put("max_time", max_time);
            bodyJSON.put("action", "load_more");
            bodyJSON.put("req_from", "drawer_loadmore");
        } else {
            bodyJSON.put("live_source", "live_small_picture");
            bodyJSON.put("action", "refresh");
            bodyJSON.put("req_from", "drawer_refresh");
        }
        // 开始处理请求
        http_get(HOST_API_VA, uri.trim(), bodyJSON, headJSON, decrypt.trim(), retval -> {
            _logString(retval.toString());
        });
    }

    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################

    // 按钮事件 : 获取用户 自身简介
    @FXML
    protected void onButtonClickByProfileME() {
        //
        String uri = "/tiktok/user/profile/self/v1";
        String decrypt = "";
        //
        JSONObject bodyJSON = new JSONObject();
        JSONObject headJSON = new JSONObject();

        bodyJSON.put("scene_id", 101);
        bodyJSON.put("profile_optimize_exp", 0);
        bodyJSON.put("exp_diff", 15);

        // 开始处理请求
        http_get(HOST_API_VA, uri.trim(), bodyJSON, headJSON, decrypt.trim(), retval -> {
            _logString(retval.toString());
        });
    }

    // 按钮事件 : 获取用户 简介信息
    @FXML
    protected void onButtonClickByProfileOther() {
        //
        String uid, suid = "";
        if ((uid = getUserId().trim()).isEmpty()) {
            AlertByE("输入异常, uid");
            return;
        }
        if ((suid = editCtrl_suid.getText().trim()).isEmpty()) {
            AlertByE("输入异常, suid");
            return;
        }
        //
        String uri = "/tiktok/user/profile/other/v1";
        String decrypt = "";
        //
        JSONObject bodyJSON = new JSONObject();
        JSONObject headJSON = new JSONObject();

        bodyJSON.put("scene_id", 201);
        bodyJSON.put("profile_optimize_exp", 0);
        bodyJSON.put("user_id", uid);
        bodyJSON.put("sec_uid", suid);
        bodyJSON.put("exp_diff", 15);

        // 开始处理请求
        http_get(HOST_API_VA, uri.trim(), bodyJSON, headJSON, decrypt.trim(), retval -> {
            _logString(retval.toString());
        });
    }

    // 按钮事件 : 获取用户 作品信息
    @FXML
    protected void onButtonClickByProfilePost() {
        //
        String uid, suid = "";
        if ((uid = getUserId().trim()).isEmpty()) {
            AlertByE("输入异常, uid");
            return;
        }
        if ((suid = editCtrl_suid.getText().trim()).isEmpty()) {
            AlertByE("输入异常, suid");
            return;
        }
        //
        String uri = "/aweme/v1/aweme/post/";
        String decrypt = "";
        //
        JSONObject bodyJSON = new JSONObject();
        JSONObject headJSON = new JSONObject();

        bodyJSON.put("count", 12); // 获取数量
        bodyJSON.put("is_other_user", 1); // 是否为 他人, 0 = 自己

        long max_cursor = 0; // 游标

        if (0 == max_cursor) {
            bodyJSON.put("locate_item_id", 0);
            bodyJSON.put("min_cursor", 0);
            bodyJSON.put("max_cursor", 0);
        } else {
            bodyJSON.put("max_cursor", max_cursor);
        }

        bodyJSON.put("user_id", uid);
        bodyJSON.put("sec_user_id", suid);

        bodyJSON.put("sort_type", 0);
        bodyJSON.put("source", 0);
        bodyJSON.put("sov_client_enable", 1);

        bodyJSON.put("user_avatar_shrink", "144_144");
        bodyJSON.put("video_cover_shrink", "372_495");

        // 开始处理请求
        http_get(HOST_API_VA, uri.trim(), bodyJSON, headJSON, decrypt.trim(), retval -> {
            _logString(retval.toString());
        });
    }

    // 按钮事件 : 获取用户 粉丝列表
    @FXML
    protected void onButtonClickByProfileFollower() {
        //
        String uid, suid = "";
        if ((uid = getUserId().trim()).isEmpty()) {
            AlertByE("输入异常, uid");
            return;
        }
        if ((suid = editCtrl_suid.getText().trim()).isEmpty()) {
            AlertByE("输入异常, suid");
            return;
        }
        //
        String uri = "/aweme/v1/user/follower/list/";
        String decrypt = "";
        //
        JSONObject bodyJSON = new JSONObject();
        JSONObject headJSON = new JSONObject();

        bodyJSON.put("count", 50);
        bodyJSON.put("max_time", 0);
        bodyJSON.put("offset", 0);
        bodyJSON.put("page_token", "");
        bodyJSON.put("sec_user_id", suid);
        bodyJSON.put("user_id", uid);
        bodyJSON.put("source_type", 1);

        // 开始处理请求
        http_get(HOST_API_VA, uri.trim(), bodyJSON, headJSON, decrypt.trim(), retval -> {
            _logString(retval.toString());
        });
    }

    // 按钮事件 : 获取用户 关注列表
    @FXML
    protected void onButtonClickByProfileFollowing() {
        //
        String uid, suid = "";
        if ((uid = getUserId().trim()).isEmpty()) {
            AlertByE("输入异常, uid");
            return;
        }
        if ((suid = editCtrl_suid.getText().trim()).isEmpty()) {
            AlertByE("输入异常, suid");
            return;
        }
        //
        String uri = "/aweme/v1/user/following/list/";
        String decrypt = "";
        //
        JSONObject bodyJSON = new JSONObject();
        JSONObject headJSON = new JSONObject();

        bodyJSON.put("count", 50);
        bodyJSON.put("live_sort_by", 1);
        bodyJSON.put("max_time", 0);
        bodyJSON.put("offset", 0);
        bodyJSON.put("page_token", "");
        bodyJSON.put("sec_user_id", suid);
        bodyJSON.put("user_id", uid);
        bodyJSON.put("source_type", 1);

        // 开始处理请求
        http_get(HOST_API_VA, uri.trim(), bodyJSON, headJSON, decrypt.trim(), retval -> {
            _logString(retval.toString());
        });
    }

    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################

    // 按钮事件 : 作品操作, 详情(描述)
    @FXML
    protected void onButtonClickByAwemeDetail() {
        //
        String aid = "";
        if ((aid = editCtrl_aweme_id.getText().trim()).isEmpty()) {
            AlertByE("输入异常, aweme_id");
            return;
        }
        //
        ArrayList<String> idS = new ArrayList<>();
        idS.add(aid);
        //
        String uri = "/aweme/v1/multi/aweme/detail/";
        String decrypt = "";
        //
        JSONObject bodyJSON = new JSONObject();
        JSONObject headJSON = new JSONObject();

        bodyJSON.put("aweme_ids", idS.toString());

        // 开始处理请求
        http_get(HOST_API_VA, uri.trim(), bodyJSON, headJSON, decrypt.trim(), retval -> {
            _logString(retval.toString());
        });
    }

    // 按钮事件 : 作品操作, 点赞
    @FXML
    protected void onButtonClickByAwemeDigg() {
        //
        String aid = "";
        if ((aid = editCtrl_aweme_id.getText().trim()).isEmpty()) {
            AlertByE("输入异常, aweme_id");
            return;
        }
        //
        String uri = "/aweme/v1/commit/item/digg/";
        String decrypt = "";
        //
        JSONObject bodyJSON = new JSONObject();
        JSONObject headJSON = new JSONObject();

        bodyJSON.put("aweme_id", aid);
        bodyJSON.put("channel_id", 0);
        bodyJSON.put("dm_share_scene", 0);
        bodyJSON.put("enter_from", "homepage_hot");
        bodyJSON.put("type", 1);

        // 开始处理请求
        http_post(HOST_API_VA, uri.trim(), bodyJSON, headJSON, decrypt.trim(), retval -> {
            _logString(retval.toString());
        });
    }

    // 按钮事件 : 作品操作, 读取评论
    @FXML
    protected void onButtonClickByAwemeCommentGET() {
        //
        String awemeID = "";
        if ((awemeID = editCtrl_aweme_id.getText().trim()).isEmpty()) {
            AlertByE("输入异常, aweme_id");
            return;
        }
        //
        String authorID = "";
        if ((authorID = editCtrl_author_id.getText().trim()).isEmpty()) {
            AlertByE("输入异常, author_id");
            return;
        }
        //
        String uri = "/aweme/v2/comment/list/";
        String decrypt = "";
        //
        JSONObject bodyJSON = new JSONObject();
        JSONObject headJSON = new JSONObject();

        bodyJSON.put("ad_pricing_type", 0);
        bodyJSON.put("author_id", authorID);
        bodyJSON.put("author_relation_type", 0);
        bodyJSON.put("aweme_id", awemeID);
        bodyJSON.put("aweme_type", 0);
        bodyJSON.put("count", 20);
        bodyJSON.put("cursor", 0); // 需要获取多页时, 此值有效
        bodyJSON.put("enable_muf_maf", 0);
        bodyJSON.put("enter_from", "homepage_hot");
        bodyJSON.put("forward_page_type", 0);
        bodyJSON.put("is_non_personalized", 0);
        bodyJSON.put("offline_pin_comment", 1);
        bodyJSON.put("query_times", 5);
        bodyJSON.put("query_times_since_last_ad", 5);

        bodyJSON.put("session_id", UUID.randomUUID().toString()); // 需要获取多页时, 此值有效

        bodyJSON.put("shown_cnt", 0);
        bodyJSON.put("source", 0);

        bodyJSON.put("user_avatar_shrink", "96_96");
        bodyJSON.put("zero_count_expire_time", "-1");

        // 开始处理请求
        http_get(HOST_API_VA, uri.trim(), bodyJSON, headJSON, decrypt.trim(), retval -> {
            _logString(retval.toString());
        });
    }

    // 按钮事件 : 作品操作, 写入评论
    @FXML
    protected void onButtonClickByAwemeCommentSET() {
        //
        String awemeID = "";
        if ((awemeID = editCtrl_aweme_id.getText().trim()).isEmpty()) {
            AlertByE("输入异常, aweme_id");
            return;
        }
        //
        String value = "";
        if ((value = editCtrl_comment.getText().trim()).isEmpty()) {
            AlertByE("输入异常, 评论内容");
            return;
        }
        //
        String uri = "/aweme/v1/comment/publish/";
        String decrypt = "";
        //
        JSONObject bodyJSON = new JSONObject();
        JSONObject headJSON = new JSONObject();

        bodyJSON.put("action_type", 0);
        bodyJSON.put("aweme_id", awemeID);
        bodyJSON.put("channel_id", 0);
        bodyJSON.put("comment_source", 0);
        bodyJSON.put("current_shown_count", 0);
        bodyJSON.put("publish_scenario", 3);
        bodyJSON.put("skip_rethink", 0);
        bodyJSON.put("text", value);

        // 开始处理请求
        http_post(HOST_API_VA, uri.trim(), bodyJSON, headJSON, decrypt.trim(), retval -> {
            _logString(retval.toString());
        });
    }

    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################
    // ############################################################################################################################################################

    // 按钮事件 : 关注操作, 通过回关
    @FXML
    protected void onButtonClickByFollowByReturn() {
        //
        String uid, suid = "";
        if ((uid = getUserId().trim()).isEmpty()) {
            AlertByE("输入异常, uid");
            return;
        }
        if ((suid = editCtrl_suid.getText().trim()).isEmpty()) {
            AlertByE("输入异常, suid");
            return;
        }
        //
        String uri = "/aweme/v1/commit/follow/user/";
        String decrypt = "";
        //
        JSONObject bodyJSON = new JSONObject();
        JSONObject headJSON = new JSONObject();

        bodyJSON.put("action_time", XPWebSocket._2TIMEStamp());
        bodyJSON.put("from", 0);
        bodyJSON.put("from_pre", -1);
        bodyJSON.put("is_network_available", true);
        bodyJSON.put("sec_user_id", suid);
        bodyJSON.put("type", 1);
        bodyJSON.put("user_id", uid);

        // 开始处理请求
        http_post(HOST_API_VA, uri.trim(), bodyJSON, headJSON, decrypt.trim(), retval -> {
            _logString(retval.toString());
        });
    }

    @FXML
    protected void onButtonClickByStopFollowByHome() {

    }

    // 按钮事件 : 关注操作, 通过主页(用户主页)
    @FXML
    protected void onButtonClickByFollowByHome() {
        //
        String uid, suid = "";
        if ((uid = getUserId().trim()).isEmpty()) {
            AlertByE("输入异常, uid");
            return;
        }
        if ((suid = editCtrl_suid.getText().trim()).isEmpty()) {
            AlertByE("输入异常, suid");
            return;
        }
        //
        String uri = "/aweme/v1/commit/follow/user/";
        String decrypt = "";
        //
        JSONObject bodyJSON = new JSONObject();
        JSONObject headJSON = new JSONObject();

        bodyJSON.put("action_time", XPWebSocket._2TIMEStamp());
        bodyJSON.put("channel_id", 3);
        bodyJSON.put("from", 19);
        bodyJSON.put("from_pre", 0);
        bodyJSON.put("is_network_available", true);
        bodyJSON.put("sec_user_id", suid);
        bodyJSON.put("type", 1);
        bodyJSON.put("user_id", uid);

        // 开始处理请求
        http_post(HOST_API_VA, uri.trim(), bodyJSON, headJSON, decrypt.trim(), retval -> {
            _logString(retval.toString());
        });
    }

    // 按钮事件 : 关注操作, 通过作品
    @FXML
    protected void onButtonClickByFollowByPost() {
        //
        String uid, suid = "";
        if ((uid = getUserId().trim()).isEmpty()) {
            AlertByE("输入异常, uid");
            return;
        }
        if ((suid = editCtrl_suid.getText().trim()).isEmpty()) {
            AlertByE("输入异常, suid");
            return;
        }
        //
        String awemeID = "";
        if ((awemeID = editCtrl_aweme_id.getText().trim()).isEmpty()) {
            AlertByE("输入异常, aweme_id");
            return;
        }
        //
        String uri = "/aweme/v1/commit/follow/user/";
        String decrypt = "";
        //
        JSONObject bodyJSON = new JSONObject();
        JSONObject headJSON = new JSONObject();

        bodyJSON.put("action_time", XPWebSocket._2TIMEStamp());
        bodyJSON.put("channel_id", 0);
        bodyJSON.put("from", 18);
        bodyJSON.put("from_pre", 0);
        bodyJSON.put("is_network_available", true);
        bodyJSON.put("item_id", awemeID);
        bodyJSON.put("sec_user_id", suid);
        bodyJSON.put("type", 1);
        bodyJSON.put("user_id", uid);

        // 开始处理请求
        http_post(HOST_API_VA, uri.trim(), bodyJSON, headJSON, decrypt.trim(), retval -> {
            _logString(retval.toString());
        });
    }

    // 按钮事件 : 关注操作, 通过推荐
    @FXML
    protected void onButtonClickByFollowByRecommend() {
        //
        String uid, suid = "";
        if ((uid = getUserId().trim()).isEmpty()) {
            AlertByE("输入异常, uid");
            return;
        }
        if ((suid = editCtrl_suid.getText().trim()).isEmpty()) {
            AlertByE("输入异常, suid");
            return;
        }
        //
        String uri = "/aweme/v1/commit/follow/user/";
        String decrypt = "";
        //
        long channel_id = 0;
        // long channel_id = 3; // 外部推荐
        // long channel_id = 26; // 粉丝数推荐
        //
        JSONObject bodyJSON = new JSONObject();
        JSONObject headJSON = new JSONObject();

        bodyJSON.put("action_time", XPWebSocket._2TIMEStamp());

        bodyJSON.put("is_network_available", true);
        bodyJSON.put("rec_type", "5-0");
        bodyJSON.put("sec_user_id", suid);
        bodyJSON.put("type", 1);
        bodyJSON.put("user_id", uid);

        if (0 != channel_id) {
            bodyJSON.put("channel_id", channel_id);
            if (3 == channel_id) {
                bodyJSON.put("from", 12);
                bodyJSON.put("from_pre", -1);
            }
            if (26 == channel_id) {
                bodyJSON.put("from", 11);
                bodyJSON.put("from_pre", 19);
            }
        } else {
            bodyJSON.put("from", 12);
            bodyJSON.put("from_pre", -1);
        }

        // 开始处理请求
        http_post(HOST_API_VA, uri.trim(), bodyJSON, headJSON, decrypt.trim(), retval -> {
            _logString(retval.toString());
        });
    }

    private void setupDragAndDrop() {
        // 拖放功能
        filePathField.setOnDragOver(e -> {
            if (e.getDragboard().hasFiles()) {
                e.acceptTransferModes(TransferMode.COPY);
                filePathField.setStyle("-fx-border-color: #4CAF50;");
            }
        });

        filePathField.setOnDragExited(e -> {
            filePathField.setStyle("-fx-border-color: null;");
        });

        filePathField.setOnDragDropped(e -> {
            Dragboard db = e.getDragboard();
            if (db.hasFiles()) {
                loadUserList(db.getFiles().get(0));
            }
            e.setDropCompleted(true);
        });

    }

    private void setupUserSelected() {
        // 示例数据

        listView_user.setPrefWidth(200);
        listView_user.setPrefHeight(150);
        popup.getContent().add(listView_user);
        popup.setAutoHide(true);

        // 输入框模糊过滤
        textCtrl_user.addEventHandler(KeyEvent.KEY_RELEASED, event -> {
            String input = textCtrl_user.getText().toLowerCase();
            List<String> filtered = userList.stream()
                    .filter(item -> item.toLowerCase().contains(input))
                    .collect(Collectors.toList());
            listView_user.setItems(FXCollections.observableArrayList(filtered));

            if (!filtered.isEmpty()) {
                Bounds bounds = textCtrl_user.localToScreen(textCtrl_user.getBoundsInLocal());
                popup.show(textCtrl_user, bounds.getMinX(), bounds.getMaxY());
            } else {
                popup.hide();
            }
        });

        // 右侧按钮点击显示全部
        dropButton_user.setOnAction(e -> {
            listView_user.setItems(userList);
            Bounds bounds = textCtrl_user.localToScreen(textCtrl_user.getBoundsInLocal());
            popup.show(textCtrl_user, bounds.getMinX(), bounds.getMaxY());
        });

        // 点击列表项
        listView_user.setOnMouseClicked(event -> {
            String selected = listView_user.getSelectionModel().getSelectedItem();
            if (selected != null) {
                textCtrl_user.setText(selected);
                popup.hide();
            }
        });
    }

    @FXML
    private void handleFileSelect(ActionEvent e) {
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("选择文件");
        fileChooser.getExtensionFilters().add(
                new FileChooser.ExtensionFilter("文本文件", "*.txt"));

        File file = fileChooser.showOpenDialog(filePathField.getScene().getWindow());
        if (file != null) {
            loadUserList(file);
        }
    }

    private void loadUserList(File file) {
        try {
            List<String> users = Files.readAllLines(file.toPath())
                    .stream()
                    .filter(line -> !line.trim().isEmpty())
                    .collect(Collectors.toList());

            filePathField.setText(file.getName());

            if (!users.isEmpty()) {
                userList.setAll(users);
                textCtrl_user.setText(users.getFirst());
                listView_user.setItems(userList);
                listView_user.toFront();
            }

        } catch (Exception e) {
            textCtrl_LogCtrl.appendText("加载文件失败: " + e.getMessage() + "\n");
        }
    }

    public List<String> getUserList() {
        return listView_user.getItems();
    }

    public String getUserId() {
        return listView_user.getSelectionModel().getSelectedItem();
    }

}
